/******************************************************************************
 * Filename : uc_cfg.c
 * Created  : 2009-10-01 by franco.yin
 * Description -
 *
 ******************************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

#include "uc_cfg.h"
#include "uc_string.h"

pthread_mutex_t uccfg_lock_cfg = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;

static int
uccfg_str_get_one(int str_index, char *pStr, void *p1, void *p2)
{
	char *pKey = *(char **)p1;
	char *pVal = *((char **)p1 + 1);
	char *pTmp = NULL;
	int key_len = **(int **)p2;
	int val_len = **((int **)p2 + 1);
	int line_len = ucstr_trim(pStr);
	int *pRet = *((int **)p2 + 2);

	if(line_len < key_len
		|| *pStr == '#'
		|| ((pTmp = strstr(pStr, pKey)) == NULL)
		|| pTmp != pStr
		|| (*(pTmp + key_len) != 0 && strchr(UCCFG_KEY_DIVIDE_RANGE, *(pTmp + key_len)) == NULL)) {
		return OK;
	}
	*pRet = OK;

	pTmp += key_len;
	if(*pTmp != 0) {
		pTmp++;
		while (*pTmp != 0 && strchr(UCCFG_KEY_DIVIDE_RANGE, *pTmp) != NULL) {
			pTmp++;
		}
		if(*pTmp != 0) {
			uc_strncpy(pVal, pTmp, val_len);
		}
	}

	return ERROR;
}

int
uccfg_str_get(char *str, char *div_str, char *key, char *value, int value_len)
{
	char *char_buf[2];
	int *int_buf[3];
	char key_tmp[UC_BUF_LINE_LEN];
	int key_len = ucstr_trim_to(key_tmp, key, sizeof(key_tmp));
	int ret = ERROR;

	if(value != NULL) {
		*value = 0;
	} else {
		uc_err("uccfg_str_get bad entry\n");
		return ERROR;
	}

	if(str == NULL || div_str == NULL || key == NULL) {
		uc_err("uccfg_str_get bad entry\n");
		return ERROR;
	}

	int_buf[0] = &key_len;
	int_buf[1] = &value_len;
	int_buf[2] = &ret;
	char_buf[0] = key_tmp;
	char_buf[1] = value;
	ucstr_divide_fun(str, div_str, uccfg_str_get_one, (void *)char_buf, (void *)int_buf);

	return ret;
}

int
uccfg_str_get_int(char *str, char *div_str, char *key, int *value)
{
	char buf[UC_BUF_LEN_SMALL];
	int ret = 0;

	ret = uccfg_str_get(str, div_str, key, buf, sizeof(buf));
	*value = atoi(buf);

	return ret;
}

static int
uccfg_str_get_all_one(int str_index, char *pStr, void *p1, void *p2)
{
	uccfg_get_all_func pFunc = *((uccfg_get_all_func *)p1);
	int *pCnt = *((int **)p1 + 1);
	char *pTmp = NULL;
	int line_len = 0;
	int ret = 0;

	line_len = ucstr_trim(pStr);
	if(line_len <= 0 || *pStr == '#') {
		return OK;
	}

	pTmp = pStr;
	while (*pTmp != 0 && strchr(UCCFG_KEY_DIVIDE_RANGE, *pTmp) == NULL) {
		pTmp++;
	}

	if (*pTmp != 0) {
		*pTmp = 0;
		pTmp++;

		while (*pTmp != 0 && strchr(UCCFG_KEY_DIVIDE_RANGE, *pTmp) != NULL) {
			pTmp++;
		}
	}

	ret = pFunc(*pCnt, pStr, pTmp, p2);
	*pCnt = *pCnt + 1;

	return ret;
}

int
uccfg_str_get_all(char *str, char *div_str, uccfg_get_all_func pFunc, void *p)
{
	void *param[2];
	int cnt = 0;

	if(str == NULL || div_str == NULL || pFunc == NULL) {
		uc_err("uccfg_str_get bad entry\n");
		return ERROR;
	}

	param[0] = (void *)pFunc;
	param[1] = (void *)&cnt;
	ucstr_divide_fun(str, div_str, uccfg_str_get_all_one, (void *)param, p);

	return cnt;
}

static int
uccfg_str_set_one(int str_index, char *pStr, void *p1, void *p2)
{
	char *pKey = *(char **)p1;
	char *pNewVal = *((char **)p1 + 1);
	char *pVal = *((char **)p1 + 2);
	char *pTmp = NULL;
	int key_len = **(int **)p2;
	int *pN = *((int **)p2 + 1);
	int *pRet = *((int **)p2 + 2);
	int line_len = ucstr_trim(pStr);

	if(line_len < key_len
		|| *pStr == '#'
		|| ((pTmp = strstr(pStr, pKey)) == NULL)
		|| pTmp != pStr
		|| (*(pTmp + key_len) != 0 && strchr(UCCFG_KEY_DIVIDE_RANGE, *(pTmp + key_len)) == NULL)) {
		if(*pStr != 0) {
			*pN = *pN + sprintf(pNewVal + *pN, "%s,", pStr);
		}
	} else {
		if(*pRet != OK) {
			*pRet = OK;
			*(pTmp + key_len) = 0;
			*pN = *pN + sprintf(pNewVal + *pN, "%s=%s,", pTmp, pVal);
		}
	}

	return OK;
}

int
uccfg_str_set(char *str, char *div_str, char *key, char *value)
{
	char *char_buf[3];
	int *int_buf[3];
	char key_tmp[UC_BUF_LINE_LEN];
	char new_value[UC_BUF_LINE_LEN * 5];
	int key_len = ucstr_trim_to(key_tmp, key, sizeof(key_tmp));
	int new_value_len = 0;
	int ret = ERROR;

	if(str == NULL || div_str == NULL || key == NULL || value == NULL) {
		uc_err("uccfg_str_set bad entry\n");
		return ERROR;
	}

	int_buf[0] = &key_len;
	int_buf[1] = &new_value_len;
	int_buf[2] = &ret;
	char_buf[0] = key_tmp;
	char_buf[1] = new_value;
	char_buf[2] = value;
	ucstr_divide_fun(str, div_str, uccfg_str_set_one, (void *)char_buf, (void *)int_buf);

	if(ret != OK) {
		new_value_len += sprintf(new_value + new_value_len, "%s=%s,", key, value);
	}

	strcpy(str, new_value);

	return OK;
}

int
uccfg_str_set_int(char *str, char *div_str, char *key, int value)
{
	char buf[UC_BUF_LEN_SMALL];

	sprintf(buf, "%d", value);
	return uccfg_str_set(str, div_str, key, buf);
}

int
uccfg_get_core(char *file, char *key, char *value, int value_len)
{
	char key_tmp[UC_BUF_LINE_LEN];
	char line[UC_BUF_LINE_LEN];
	char *pTmp = NULL;
	FILE *fp = NULL;
	int line_len = 0;
	int key_len = 0;
	int ret = ERROR;

	if(value != NULL) {
		*value = 0;
	} else {
		uc_err("uccfg_get_core bad entry\n");
		return ERROR;
	}

	if(file == NULL || key == NULL) {
		uc_err("uccfg_get_core bad entry\n");
		return ERROR;
	}

	if ((fp = fopen(file, "r")) == NULL) {
		return ERROR;
	}

	ucstr_trim_to(key_tmp, key, sizeof(key_tmp));
	key_len = strlen(key_tmp);

	while (fgets(line, sizeof(line), fp) != NULL) {
		line_len = ucstr_trim(line);
		if(line_len < key_len
			|| line[0] == '#'
			|| ((pTmp = strstr(line, key_tmp)) == NULL)
			|| pTmp != line
			|| (*(pTmp + key_len) != 0 && strchr(UCCFG_KEY_DIVIDE_RANGE, *(pTmp + key_len)) == NULL)) {
			continue;
		}
		ret = OK;

		pTmp += key_len;
		if(*pTmp != 0) {
			pTmp++;
			while (*pTmp != 0 && strchr(UCCFG_KEY_DIVIDE_RANGE, *pTmp) != NULL) {
				pTmp++;
			}
			if(*pTmp != 0) {
				uc_strncpy(value, pTmp, value_len);
			}
		}
		break;
	}

	fclose(fp);

	return ret;
}

int
uccfg_get_all_core(char *file, uccfg_get_all_func pFunc, void *p)
{
	char line[UC_BUF_LINE_LEN];
	char *pTmp = NULL;
	FILE *fp = NULL;
	int line_len = 0;
	int cnt = 0;

	if(file == NULL || pFunc == NULL) {
		uc_err("uccfg_get_all_core bad entry\n");
		return ERROR;
	}

	if ((fp = fopen(file, "r")) == NULL) {
		return ERROR;
	}

	while (fgets(line, sizeof(line), fp) != NULL) {
		line_len = ucstr_trim(line);
		if(line_len <= 0 || line[0] == '#') {
			continue;
		}

		pTmp = line;
		while (*pTmp != 0 && strchr(UCCFG_KEY_DIVIDE_RANGE, *pTmp) == NULL) {
			pTmp++;
		}

		if (*pTmp != 0) {
			*pTmp = 0;
			pTmp++;

			while (*pTmp != 0 && strchr(UCCFG_KEY_DIVIDE_RANGE, *pTmp) != NULL) {
				pTmp++;
			}
		}
		if(pFunc(cnt, line, pTmp, p) == ERROR) {
			cnt++;
			break;
		}
		cnt++;
	}

	fclose(fp);

	return cnt;
}

int
uccfg_set_core(char *file, char *key, char div_char, char *value)
{
	char key_tmp[UC_BUF_LINE_LEN];
	char line[UC_BUF_LINE_LEN];
	char buf[UC_BUF_LINE_LEN];
	char *pTmp = NULL;
	FILE *fp = NULL;
	int file_size = 0;
	int key_len = 0;
	int line_len = 0;
	int read_len = 0;
	int ret = ERROR;
	int pos1 = 0;
	int pos2 = 0;

	if(file == NULL || key == NULL || value == NULL) {
		uc_err("uccfg_set_core bad entry\n");
		return ERROR;
	}

	if (ucfile_exist(file)) {
		file_size = ucfile_size(file);
		if(file_size < 0) {
			return ERROR;
		}
		fp = fopen(file, "r+");
	} else {
		fp = fopen(file, "w+");
	}
	if (fp == NULL) {
		return ERROR;
	}

	ucstr_trim_to(key_tmp, key, sizeof(key_tmp));
	key_len = strlen(key_tmp);

	pos1 = ftell(fp);
	while (fgets(line, sizeof(line), fp) != NULL) {
		pos2 = ftell(fp);
		line_len = ucstr_trim(line);
		if(line_len < key_len
			|| line[0] == '#'
			|| (pTmp = strstr(line, key_tmp)) == NULL
			|| pTmp != line
			|| (*(pTmp + key_len) != 0 && strchr(UCCFG_KEY_DIVIDE_RANGE, *(pTmp + key_len)) == NULL)) {
			pos1 = pos2;
			continue;
		}

		pTmp = uc_malloc(file_size + 10);
		if(pTmp == NULL){
			uc_err("set cfg file[%s] key[%s] val[%s] malloc error\n", file, key, value);
			fclose(fp);
			return ERROR;
		}
		read_len = fread(pTmp, 1, file_size, fp);
		fseek(fp, pos1, SEEK_SET);
		ucstr_trim_to(buf, value, sizeof(buf));
		if(div_char <= 0) {
			fprintf(fp, "%s%c%s\n", key_tmp, UCCFG_KEY_DIVIDE_DEFAULT, buf);
		} else {
			fprintf(fp, "%s%c%s\n", key_tmp, div_char, buf);
		}
		fwrite(pTmp, read_len, 1, fp);
		truncate(file, ftell(fp));

		uc_free(pTmp);
		ret = OK;
		break;
	}

	if(ret != OK) {
		ucstr_trim_to(buf, value, sizeof(buf));
		if(div_char <= 0) {
			fprintf(fp, "%s%c%s\n", key_tmp, UCCFG_KEY_DIVIDE_DEFAULT, buf);
		} else {
			fprintf(fp, "%s%c%s\n", key_tmp, div_char, buf);
		}
	}

	fflush(fp);
	fclose(fp);

	return OK;
}

int
uccfg_del_core(char *file, char *key)
{
	char key_tmp[UC_BUF_LINE_LEN];
	char line[UC_BUF_LINE_LEN];
	char *pTmp = NULL;
	FILE *fp = NULL;
	int file_size = 0;
	int key_len = 0;
	int line_len = 0;
	int read_len = 0;
	int pos1 = 0;
	int pos2 = 0;

	if(file == NULL || key == NULL) {
		uc_err("uccfg_del_core bad entry\n");
		return ERROR;
	}

	if(!ucfile_exist(file)) {
		return OK;
	}

	file_size = ucfile_size(file);
	if(file_size < 0) {
		return ERROR;
	}
	fp = fopen(file, "r+");
	if (fp == NULL) {
		return ERROR;
	}

	ucstr_trim_to(key_tmp, key, sizeof(key_tmp));
	key_len = strlen(key_tmp);

	pos1 = ftell(fp);
	while (fgets(line, sizeof(line), fp) != NULL) {
		pos2 = ftell(fp);
		line_len = ucstr_trim(line);
		if(line_len < key_len
			|| line[0] == '#'
			|| (pTmp = strstr(line, key_tmp)) == NULL
			|| pTmp != line
			|| (*(pTmp + key_len) != 0 && strchr(UCCFG_KEY_DIVIDE_RANGE, *(pTmp + key_len)) == NULL)) {
			pos1 = pos2;
			continue;
		}

		pTmp = uc_malloc(file_size + 10);
		if(pTmp == NULL){
			uc_err("del cfg file[%s] key[%s] malloc error\n", file, key);
			fclose(fp);
			return ERROR;
		}
		read_len = fread(pTmp, 1, file_size, fp);
		fseek(fp, pos1, SEEK_SET);
		fwrite(pTmp, read_len, 1, fp);
		truncate(file, ftell(fp));
		fflush(fp);

		uc_free(pTmp);
		break;
	}

	fclose(fp);

	return OK;
}

int
uccfg_get(char *file, uccfg_src_s *pArr, int id, char *value, int value_len)
{
	int ret = OK;
	int lock_fd = 0;

	if(!ucfile_exist(file)) {
		uc_strncpy(value, pArr[id].default_value, value_len);
		return ERROR;
	}

	lock_fd = ucfile_rlock(file);
	pthread_mutex_lock(&uccfg_lock_cfg);

	if(uccfg_get_core(file, pArr[id].name, value, value_len) != OK) {
		uc_strncpy(value, pArr[id].default_value, value_len);
		ret = ERROR;
	}

	pthread_mutex_unlock(&uccfg_lock_cfg);
	ucfile_unlock(lock_fd);

	return ret;
}

int
uccfg_set(char *file, uccfg_src_s *pArr, int id, char *value)
{
	int ret = 0;
	int lock_fd = 0;

	lock_fd = ucfile_wlock(file);
	pthread_mutex_lock(&uccfg_lock_cfg);

	ret = uccfg_set_core(file, pArr[id].name, 0, value);

	pthread_mutex_unlock(&uccfg_lock_cfg);
	ucfile_unlock(lock_fd);

	return ret;
}

int
uccfg_get_int(char *file, uccfg_src_s *pArr, int id, int *value)
{
	char buf[UC_BUF_LEN_SMALL];
	int ret = 0;

	ret = uccfg_get(file, pArr, id, buf, sizeof(buf));
	*value = atoi(buf);

	return ret;
}

int
uccfg_set_int(char *file, uccfg_src_s *pArr, int id, int value)
{
	char buf[UC_BUF_LEN_SMALL];

	sprintf(buf, "%d", value);
	return uccfg_set(file, pArr, id, buf);
}

int
uccfg_get_by_key(char *file, char *key, char *value, int value_len)
{
	int ret = OK;
	int lock_fd = 0;

	lock_fd = ucfile_rlock(file);
	pthread_mutex_lock(&uccfg_lock_cfg);

	ret = uccfg_get_core(file, key, value, value_len);

	pthread_mutex_unlock(&uccfg_lock_cfg);
	ucfile_unlock(lock_fd);

	return ret;
}


int
uccfg_set_by_key(char *file, char *key, char *value)
{
	int ret = 0;
	int lock_fd = 0;

	lock_fd = ucfile_wlock(file);
	pthread_mutex_lock(&uccfg_lock_cfg);

	ret = uccfg_set_core(file, key, 0, value);

	pthread_mutex_unlock(&uccfg_lock_cfg);
	ucfile_unlock(lock_fd);

	return ret;
}

int
uccfg_set_by_key_ext(char *file, char *key, char div_char, char *value)
{
	int ret = 0;
	int lock_fd = 0;

	lock_fd = ucfile_wlock(file);
	pthread_mutex_lock(&uccfg_lock_cfg);

	ret = uccfg_set_core(file, key, div_char, value);

	pthread_mutex_unlock(&uccfg_lock_cfg);
	ucfile_unlock(lock_fd);

	return ret;
}

int
uccfg_get_by_key_int(char *file, char *key, int *value)
{
	char buf[UC_BUF_LEN_SMALL];
	int ret = 0;

	ret = uccfg_get_by_key(file, key, buf, sizeof(buf));
	*value = atoi(buf);
	return ret;
}

int
uccfg_set_by_key_int(char *file, char *key, int value)
{
	char buf[UC_BUF_LEN_SMALL];

	sprintf(buf, "%d", value);
	return uccfg_set_by_key(file, key, buf);
}

int
uccfg_set_by_key_int_ext(char *file, char *key, char div_char, int value)
{
	char buf[UC_BUF_LEN_SMALL];

	sprintf(buf, "%d", value);
	return uccfg_set_by_key_ext(file, key, div_char, buf);
}

int
uccfg_get_all(char *file, uccfg_get_all_func pFunc, void *p)
{
	int cnt = 0;
	int lock_fd = 0;

	lock_fd = ucfile_rlock(file);
	pthread_mutex_lock(&uccfg_lock_cfg);

	cnt = uccfg_get_all_core(file, pFunc, p);

	pthread_mutex_unlock(&uccfg_lock_cfg);
	ucfile_unlock(lock_fd);

	return cnt;
}

int
uccfg_del_by_key(char *file, char *key)
{
	int ret = 0;
	int lock_fd = 0;

	lock_fd = ucfile_wlock(file);
	pthread_mutex_lock(&uccfg_lock_cfg);

	ret = uccfg_del_core(file, key);

	pthread_mutex_unlock(&uccfg_lock_cfg);
	ucfile_unlock(lock_fd);

	return ret;
}

int
uccfg_creat_cfg_file(char *file, uccfg_src_s *pArr)
{
	int i = 0;

	remove(file);

	while(1) {
		if(strlen(pArr[i].name) <= 0) {
			break;
		}
		uccfg_set_by_key(file, pArr[i].name, pArr[i].default_value);
		i++;
	}
	sync();

	return OK;
}

#ifdef __cplusplus
}
#endif
